#include <AutoLock.hh>
#include "SignalTest.hh"

Mutex SignalTest::Key;
Number SignalTest::Var = 0;

ResultOf SignalTest::testBasicCase(Signal* Target)
{
	if (Target == NULL)
	{
		notifyError(Target, "Bad parameter.");
		return RETURN_PARAM_ERROR;
	}
	
	Var = 1234;
	
	ResultOf Result;
	const Number Num = 100;
	LinkedList<pthread_t>* Sender;
	LinkedList<pthread_t>* Receiver;
	
	// NOTE
	// 시그널이 발생한 후에 시그널을 처리할 수 있는지 검증.
	
	Result = Target->post();
	checkErrorWR(Result, "Fail Target->post.");
	
	Result = Target->pend();
	checkErrorWR(Result, "Fail Target->pend.");
	
	if (Target->tellWaiting() != 0)
	{
		notifyError(Target->tellWaiting(), "Not exiting completely.");
		return RETURN_ERROR;
	}
	
	// NOTE
	// 여러 스레드가 대기할 경우 시그널이 전달되는지 검증.
	
	Receiver = createThread(Num, receiveSignal, (void*)Target);
	checkNullWR(Receiver, "Receiver is NULL.");
	
	usleep(100000); // 0.1초
	if (Var != 1234)
	{
		notifyError(Var, "Not work pend method.");
		return RETURN_ERROR;
	}
	
	for (Number LC = 0; LC < Num; LC++)
	{
		Result = Target->post();
		checkErrorWOR(Result, "Fail Target->post.");
	}
	
	Result = destoryThread(Receiver);
	checkErrorWR(Result, "Fail destoryThread.");
	
	if (Target->tellWaiting() != 0)
	{
		notifyError(Target->tellWaiting(), "Not exiting completely.");
		return RETURN_ERROR;
	}
	
	if (Var != 1234 + Num)
	{
		notifyError(Var, "The resulting value is incorrect.");
		return RETURN_ERROR;
	}
	
	// NOTE
	// 여러 스레드가 시그널을 발생시키고 여러 스레드가 대기할 경우
	// 시그널이 전달되는지 검증.
	
	Var = 1234;
	
	Receiver = createThread(Num, receiveSignal, (void*)Target);
	checkNullWR(Receiver, "Receiver is NULL.");
	
	usleep(100000); // 0.1초
	if (Var != 1234)
	{
		notifyError(Var, "Not work pend method.");
		return RETURN_ERROR;
	}
	
	Sender = createThread(Num, sendSignal, (void*)Target);
	checkNullWR(Sender, "Sender is NULL.");
	
	Result = destoryThread(Receiver);
	checkErrorWR(Result, "Fail destoryThread.");
	
	Result = destoryThread(Sender);
	checkErrorWR(Result, "Fail destoryThread.");
	
	if (Target->tellWaiting() != 0)
	{
		notifyError(Target->tellWaiting(), "Not exiting completely.");
		return RETURN_ERROR;
	}
	
	if (Var != 1234 + Num)
	{
		notifyError(Var, "The resulting value is incorrect.");
		return RETURN_ERROR;
	}
	
	return RETURN_OK;
}

ResultOf SignalTest::testRelease(Signal* Target)
{
	Var = 1234;
	
	ResultOf Result;
	const Number Num = 100;
	LinkedList<pthread_t>* Receiver;
	
	Receiver = createThread(Num, receiveSignal, (void*)Target);
	checkNullWR(Receiver, "Receiver is NULL.");
	
	usleep(100000); // 0.1초
	if (Var != 1234)
	{
		notifyError(Var, "Not work pend method.");
		return RETURN_ERROR;
	}
	
	Result = Target->release();
	checkErrorWR(Result, "Fail Target->release.");
	
	Result = destoryThread(Receiver);
	checkErrorWR(Result, "Fail destoryThread.");
	
	if (Target->tellWaiting() != 0)
	{
		notifyError(Target->tellWaiting(), "Not exiting completely.");
		return RETURN_ERROR;
	}
	
	if (Var != 1234 + Num)
	{
		notifyError(Var, "The resulting value is incorrect.");
		return RETURN_ERROR;
	}
	
	return RETURN_OK;
}

ResultOf SignalTest::testPreventWaiting(Signal* Target)
{
	Var = 1234;
	
	ResultOf Result;
	const Number Num = 100;
	const Number Num2 = 13;
	LinkedList<pthread_t>* Receiver;
	
	for (Number LC = 0; LC < Num2; LC++)
	{
		Result = Target->post();
		checkErrorWR(Result, "Fail Target->post.");
	}
	
	Result = Target->preventWaiting();
	checkErrorWR(Result, "Fail Target->preventWaiting.");
	
	Receiver = createThread(Num, receiveSignal, (void*)Target);
	checkNullWR(Receiver, "Receiver is NULL.");
	
	Result = destoryThread(Receiver);
	checkErrorWR(Result, "Fail destoryThread.");
	
	if (Target->tellWaiting() != 0)
	{
		notifyError(Target->tellWaiting(), "Not exiting completely.");
		return RETURN_ERROR;
	}
	
	if (Var != 1234 + Num2)
	{
		notifyError(Var, "The resulting value is incorrect.");
		return RETURN_ERROR;
	}
	
	return RETURN_OK;
}

void* SignalTest::receiveSignal(void* Param)
{
	Signal* MySignal;
	ResultOf Result;
	
	MySignal = (Signal*)Param;
	
	Result = MySignal->pend();
	if (checkError(Result))
	{
		if (Result != RETURN_BLOCK)
			notifyError(Result, "Fail MySignal->pend.");
		
		return NULL;
	}
	
	AutoLock AutoKey(&Key);
	++Var;
	
	return NULL;
}

void* SignalTest::sendSignal(void* Param)
{
	Signal* MySignal;
	ResultOf Result;
	
	MySignal = (Signal*)Param;
	
	Result = MySignal->post();
	checkErrorWOR(Result, "Fail MySignal->post.");
	
	return NULL;
}

ResultOf testCondVar()
{
	ResultOf Result;
	CondVar MySignal;
	SignalTest Test;
	
	Result = Test.testBasicCase(&MySignal);
	checkErrorWR(Result, "Fail Test.testBasicCase.");
	
	Result = Test.testRelease(&MySignal);
	checkErrorWR(Result, "Fail Test.testRelease.");
	
	Result = Test.testPreventWaiting(&MySignal);
	checkErrorWR(Result, "Fail Test.testPreventWaiting.");
	
	fprintf(stderr, "CondVar: Test success.\n");
	return RETURN_OK;
}
